這是一個重大的架構改進。我們需要將原本線性的 historyFEN 陣列轉變為樹狀結構(Tree Structure),並引入一個「當前節點」的概念來追蹤玩家在棋譜樹中的位置。
以下是實現這些功能的修改方案:
首先,定義新的數據結構並修改初始化邏輯。
JavaScript
// --- New JSON Tree Structure ---
let gameData = {
position: INITIAL_FEN,
move: [], // Root level moves
comment: ""
};
// Track current position in the tree
// currentNode: reference to the move object {m, v, c}
// parentNode: reference to the array containing the current move object
let currentNode = null;
let currentPath = []; // Array of indices to reach the current move
當走棋時,根據目前是否處於該分支的末尾來決定是「追加」還是「新增分支」。
JavaScript
function pushHistory(moveText) {
const newMove = { m: moveText };
let targetList;
// Determine where to insert the new move
if (currentStepIndex === -1) {
// We are at the starting position
targetList = gameData.move;
} else {
// We are at some move node, new moves go into its 'v' (variations)
if (!currentNode.v) currentNode.v = [];
targetList = currentNode.v;
}
// Check if this move already exists in the variations
let existingMove = targetList.find(item => item.m === moveText);
if (!existingMove) {
// Add as a new branch/variation
targetList.push(newMove);
currentNode = newMove;
} else {
// Move already exists, just switch to that branch
currentNode = existingMove;
}
// Update path and indices
// (Logic to update currentPath and sync with UI...)
renderRecordUI();
}
在 style 標籤中加入分支標記與選單的樣式。
CSS
/* --- Branch Marker Styling --- */
.branch-marker {
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
background-color: #8b0000; /* Dark Red */
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
font-size: 9px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
z-index: 10;
}
/* --- Branch Dropdown Menu --- */
.branch-menu {
position: absolute;
background: #444;
border: 1px solid #666;
box-shadow: 0 4px 8px rgba(0,0,0,0.5);
z-index: 100;
min-width: 150px;
padding: 5px 0;
}
.branch-menu-item {
display: flex;
align-items: center;
padding: 5px 10px;
gap: 10px;
cursor: pointer;
}
.branch-menu-item.active { background: #3d5afe; }
.branch-menu-item:hover { background: #555; }
.btn-del-branch { color: #ff5252; margin-left: auto; }
這部分處理點擊標記後的彈出選單以及「序號點擊排序」的特殊邏輯。
JavaScript
let selectedSortIdx = null;
function showBranchMenu(anchorEl, variations, currentIndex) {
const menu = document.createElement('div');
menu.className = 'branch-menu';
variations.forEach((v, i) => {
const item = document.createElement('div');
item.className = `branch-menu-item ${i === currentIndex ? 'active' : ''}`;
// Index Label (for sorting)
const idxLabel = document.createElement('span');
idxLabel.textContent = i + 1;
idxLabel.onclick = (e) => {
e.stopPropagation();
handleBranchSort(variations, i);
};
// Move Text
const moveTxt = document.createElement('span');
moveTxt.textContent = v.m;
moveTxt.onclick = () => { /* Switch to this branch */ };
// Delete Button
const delBtn = document.createElement('span');
delBtn.innerHTML = '×';
delBtn.className = 'btn-del-branch';
delBtn.onclick = (e) => {
e.stopPropagation();
variations.splice(i, 1);
renderRecordUI();
};
item.append(idxLabel, moveTxt, delBtn);
menu.appendChild(item);
});
// Position menu below anchorEl
document.body.appendChild(menu);
}
function handleBranchSort(variations, clickedIdx) {
if (selectedSortIdx === null) {
selectedSortIdx = clickedIdx;
// Visual feedback for selection...
} else {
if (selectedSortIdx !== clickedIdx) {
const element = variations.splice(selectedSortIdx, 1)[0];
// If x < y, move x below y. If x > y, move x above y.
variations.splice(clickedIdx, 0, element);
}
selectedSortIdx = null;
renderRecordUI();
}
}
實現 JSON 下載與讀取功能。
JavaScript
// --- Tool Save ---
document.getElementById('tool-save').addEventListener('click', () => {
const timestamp = new Date().toISOString().replace(/[-T:Z]/g, '').slice(0, 14);
const fileName = `ejcees_json_${timestamp}.txt`;
const blob = new Blob([JSON.stringify(gameData, null, 2)], { type: 'text/plain' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = fileName;
a.click();
});
// --- Tool Open ---
document.getElementById('tool-open').addEventListener('click', () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.txt';
input.onchange = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (event) => {
try {
gameData = JSON.parse(event.target.result);
// Reset state and jump to start
currentStepIndex = -1;
loadFEN(gameData.position);
renderRecordUI();
} catch (err) {
alert("Invalid JSON format");
}
};
reader.readAsText(file);
};
input.click();
});
這套改進將使你的棋盤程式具備專業的研究功能,支援多重分支與導出標準棋譜。